/*====================================================*/
/* Copyright 2011 Justin Middleton                    */
/* License: Eclipse Public License v1.0,              */
/*          http://www.eclipse.org/legal/epl-v10.html */
/*====================================================*/



/* ============================================================
 * Packs a value between 0 and 65535 into a two-byte array
 * in big-endian (network) order.
 */

this.packShort = function (inValue) {
    byte1 = (inValue & 0xFFFF) >> 8
    byte2 = (inValue & 0xFF)
    return [byte1, byte2]
}

/* ============================================================
 * Takes a numeric or character value, returns the low byte.
 */
this.packChar = function (inValue) {
    return (inValue & 0xFF)
}

/*============================================================
 * Packs a number into the number of bytes specified by width,
 * in network (big-endian) order.  If width is one or two,
 * delegates to packChar() or packShort()
 */

this.packNumber = function (width, value) {
    if (width == 1) {
        return packChar(value)
    }
    if (width == 2) {
        return packShort(value)
    }
    ret = []
    var count = 0
    while (value && (count < width)) {
        ++count
        ret.push(value & 0xFF);
        value = value >> 8;
    }
    pad = width - count
    while (pad > 0) {
        --pad
        ret.push(0)
    }
    return ret.reverse()
}

/* ============================================================
 * pack an IPv4 address/dotted quad.  this must be in w.x.y.z format.
 */

this.packIPV4Address = function (value) {
    bytes = value.split(".").map(function(x,i,a) {return (new Number(x)).valueOf()})
    badBytes = bytes.filter(function(x) {return (x & 0xFF) != x})
    if (badBytes.length > 0) {
        throw new Error ("Illegal IPv4 address [" + value + "] passed to packIPV4Address.")
    }
    return bytes
}

this.testpackIPV4Address = function () {
    
    failures = []
    
    address         = "255.255.255.255"
    packedAddress   = [0xFF, 0xFF, 0xFF, 0xFF]
    packResult = this.packIPV4Address(address)
    if (!packedAddress.equals(packResult)) {
        failures.push("testpackIPV4Address.Test1 -- FAIL: got " + packResult)
    }
    
    address         = "0.0.0.0"
    packedAddress   = [0x00, 0x00, 0x00, 0x00]
    packResult = this.packIPV4Address(address)
    if (!packedAddress.equals(packResult)) {
        failures.push("testpackIPV4Address.Test2 -- FAIL: got " + packResult)
    }
    
    // these shouldn't pack.  instead we should get an exception that
    // they are illegal addresses.
    
    address = "350.2.33.0"
    try {
        packResult = this.packIPV4Address(address)
        failures.push("testpackIPV4Address3 -- FAIL: expected exception, got " + packResult)
    } catch (e) {
        // nothing... we're good.
    }

    address = "0.9999.33.0"
    try {
        packResult = this.packIPV4Address(address)
        failures.push("testpackIPV4Address4 -- FAIL: expected exception, got " + packResult)
    } catch (e) {
        // nothing... we're good.
    }

    address = "0.a.33.0"
    try {
        packResult = this.packIPV4Address(address)
        failures.push("testpackIPV4Address5 -- FAIL: expected exception, got " + packResult)
    } catch (e) {
        // nothing... we're good.
    }

    address = "a.b.c.d"
    try {
        packResult = this.packIPV4Address(address)
        failures.push("testpackIPV4Address6 -- FAIL: expected exception, got " + packResult)
    } catch (e) {
        // nothing... we're good.
    }
    
    return failures
}

this.packString = function (str) {
    // lifted from http://stackoverflow.com/questions/1240408/reading-bytes-from-a-javascript-string
    var ch, st, re = [];
    for (var i = 0; i < str.length; i++ ) {
        ch = str.charCodeAt(i);  // get char 
        st = [];                 // set up "stack"
        do {
          st.push( ch & 0xFF );  // push byte to stack
          ch = ch >> 8;          // shift value down by 1 byte
        }  
        while ( ch );
        // add stack contents to result
        // done because chars have "wrong" endianness
        re = re.concat( st.reverse() );
    }
    // return an array of bytes
    return re;
}

this.runTests = function() {
    failures = []
    failures.concat(this.testpackIPV4Address())
    return failures
}
